package org.ariadne_eu.hcifetcher.rest;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.ariadne_eu.hcifetcher.util.DateManager;
import org.joda.time.DateTime;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.EntityNotFoundException;
import com.google.appengine.api.datastore.FetchOptions;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.datastore.Text;

public class RestUtils {

	protected static RestUtils instance = null;

	private static final Logger log = Logger.getLogger(RestUtils.class
			.getName());

	public RestUtils() {

	}

	public static RestUtils getInstance() {
		if (instance == null)
			instance = new RestUtils();
		return instance;
	}

	public String getEntries(String type, String set, String author, int size) {
		JSONObject json = new JSONObject();

		JSONArray results = new JSONArray(getEntriesJSONBySetAndAuthor(type,
				set, author, null, size));

		try {
			json.put("total_results", results.length());
			json.put("results", results);
		} catch (JSONException e) {
			log.log(Level.SEVERE, "JSONException", e);
		}
		
		

		return json.toString();
	}
	
	public String getEntriesForEnrollment(String type, String student, String course, int offset, int size) {
		JSONObject json = new JSONObject();
		JSONArray results = new JSONArray(getEntriesByStudentAndCourse(student,course,null,offset,size));
		try {
			json.put("total_results", results.length());
			json.put("results", results);
		} catch (JSONException e) {
			log.log(Level.SEVERE, "JSONException", e);
		}

		return json.toString();
	}
	
	public String getCourseByName(String name) {
		JSONObject json = new JSONObject();
		JSONArray results = new JSONArray(getEntriesJSON("Course", "name", name, null, 0, 2, "name", true));
		try {
			json.put("total_results", results.length());
			json.put("results", results);
		} catch (JSONException e) {
			log.log(Level.SEVERE, "JSONException", e);
		}

		return json.toString();
	}

	public Vector<JSONObject> getEntriesJSON(String type, String attribute,
			String value, String formatStr) {
		return getEntriesJSON(type, attribute, value, formatStr, 0, 10000, null, false);
	}

	public Vector<JSONObject> getEntriesJSONBySetAndAuthor(String type,
			String set, String author, String formatStr, int size) {
		return getEntriesJSONBySetAndAuthor(type, set, author, formatStr, 0,
				size);
	}

	/**
	 * 
	 * @param type
	 *            type entry om op te halen
	 * @param attribute
	 *            is het attribuut waarop gefilterd moet worden (hashtags)
	 * @param value
	 *            waarde waaraan de attribute in query moet voldoen om opgenomen
	 *            te worden
	 * @param formatStr
	 * @param offset
	 * @param size
	 *            het aantal entries die zullen worden teruggegeven
	 * @return
	 */
	public Vector<JSONObject> getEntriesJSON(String type, String attribute,
			String value, String formatStr, int offset, int size, String sortField, boolean ascending) {
		Vector<JSONObject> results = new Vector<JSONObject>();
		if (offset < 0 || size < 1)
			return results;
		boolean export = false;
		if (formatStr != null && formatStr.equalsIgnoreCase("export")) {
			export = true;
		}
		try {
			DatastoreService datastore = DatastoreServiceFactory
					.getDatastoreService();
			Query query = null;
			if (type == null) {
				query = new Query();
			} else {
				query = new Query(type);
			}
			if (attribute != null && !attribute.trim().equals("")
					&& value != null && !value.trim().equals("")) {
				query.addFilter(attribute, Query.FilterOperator.EQUAL, value);
			}
			if(sortField != null){
				if(!ascending)
					query.addSort(sortField, Query.SortDirection.DESCENDING);
				else
					query.addSort(sortField, Query.SortDirection.ASCENDING);
			}
			List<Entity> result = datastore.prepare(query).asList(
					FetchOptions.Builder.withLimit(size).offset(offset));
			for (Entity entity : result) {
				JSONObject ent = new JSONObject();
				Map<String, Object> props = new HashMap<String, Object>();
				props.putAll(entity.getProperties());
				props.put("id", entity.getKey());
				if (export) {
					ent.put("key", KeyFactory.keyToString(entity.getKey()));
					ent.put("entitytype", type);
				}
				for (String key : props.keySet()) {
					Object object = props.get(key);
					String value2 = "";
					if (object != null)
						value2 = object.toString();
					if (object instanceof Text)
						value2 = ((Text) object).getValue();
					if (object instanceof Date)
						value2 = DateManager.fmt1.print(new DateTime(
								(Date) object));
					ent.put(key, value2);
					if (export)
						ent.put(key + "_type", object.getClass()
								.getCanonicalName());
				}
				results.add(ent);
			}
		} catch (JSONException e) {
			log.log(Level.SEVERE, "JSONException", e);
		}
		return results;
	}
	
	public Vector<JSONObject> getEntriesJSON(String type, Map<String, Object> filterAtrributes, int offset, int size, String sortField, boolean ascending) {
		Vector<JSONObject> results = new Vector<JSONObject>();
		if (offset < 0 || size < 1)
			return results;
		boolean export = false;
		try {
			DatastoreService datastore = DatastoreServiceFactory
					.getDatastoreService();
			Query query = null;
			if (type == null) {
				query = new Query();
			} else {
				query = new Query(type);
			}
			for(String attribute : filterAtrributes.keySet()){
				Object value = filterAtrributes.get(attribute);
				if (attribute != null && !attribute.trim().equals("")
						&& value != null) {
					if(value instanceof String && !((String)value).trim().equals(""))
						value = ((String)value).trim();
					query.addFilter(attribute, Query.FilterOperator.EQUAL, value);
				}
			}
			if(sortField != null){
				if(!ascending)
					query.addSort(sortField, Query.SortDirection.DESCENDING);
				else
					query.addSort(sortField, Query.SortDirection.ASCENDING);
			}
			List<Entity> result = datastore.prepare(query).asList(
					FetchOptions.Builder.withLimit(size).offset(offset));
			for (Entity entity : result) {
				JSONObject ent = new JSONObject();
				Map<String, Object> props = new HashMap<String, Object>();
				props.putAll(entity.getProperties());
				props.put("id", entity.getKey());
				if (export) {
					ent.put("key", KeyFactory.keyToString(entity.getKey()));
					ent.put("entitytype", type);
				}
				for (String key : props.keySet()) {
					Object object = props.get(key);
					String value2 = "";
					if (object != null)
						value2 = object.toString();
					if (object instanceof Text)
						value2 = ((Text) object).getValue();
					if (object instanceof Date)
						value2 = DateManager.fmt1.print(new DateTime(
								(Date) object));
					ent.put(key, value2);
					if (export)
						ent.put(key + "_type", object.getClass()
								.getCanonicalName());
				}
				results.add(ent);
			}
		} catch (JSONException e) {
			log.log(Level.SEVERE, "JSONException", e);
		}
		return results;
	}

	// To export
	public Vector<JSONObject> getEntriesJSONBySetAndAuthor(String type,
			String set, String author, String formatStr, int offset, int size) {
		Vector<JSONObject> results = new Vector<JSONObject>();
		if (offset < 0 || size < 1)
			return results;
		boolean export = false;
		if (formatStr != null && formatStr.equalsIgnoreCase("export")) {
			export = true;
		}
		try {
			DatastoreService datastore = DatastoreServiceFactory
					.getDatastoreService();
			Query query = null;
			if (type == null) {
				query = new Query();
			} else {
				query = new Query(type);
			}
			if (set != null && !set.trim().equals("")) {
				query.addFilter("set", Query.FilterOperator.EQUAL, set);
			}
			if (author != null && !author.trim().equals("")) {
				query.addFilter("author", Query.FilterOperator.EQUAL, author);
			}
			query.addSort("datestamp",Query.SortDirection.DESCENDING);
			List<Entity> result = datastore.prepare(query).asList(
					FetchOptions.Builder.withLimit(size).offset(offset));
			for (Entity entity : result) {
				JSONObject ent = new JSONObject();
				Map<String, Object> props = new HashMap<String, Object>();
				props.putAll(entity.getProperties());
				props.put("id", entity.getKey());
				if (export) {
					ent.put("key", KeyFactory.keyToString(entity.getKey()));
					ent.put("entitytype", type);
				}
				for (String key : props.keySet()) {
					Object object = props.get(key);
					String value = "";
					if (object != null)
						value = object.toString();
					if (object instanceof Text)
						value = ((Text) object).getValue();
					if (object instanceof Date)
						value = DateManager.fmt1.print(new DateTime(
								(Date) object));
					ent.put(key, value);
					if (export)
						ent.put(key + "_type", object.getClass()
								.getCanonicalName());
				}
				results.add(ent);
			}
		} catch (JSONException e) {
			log.log(Level.SEVERE, "JSONException", e);
		}
		return results;
	}

	public String getUrlAsString(URL url, Charset charSet) {
		String result = "";
		try {
			BufferedReader reader = null;
			if (charSet == null) {
				reader = new BufferedReader(new InputStreamReader(url
						.openStream()));
			} else {
				reader = new BufferedReader(new InputStreamReader(url
						.openStream(), charSet));
			}
			String line;

			while ((line = reader.readLine()) != null) {
				result += line + "\n";
			}
			reader.close();

		} catch (MalformedURLException e) {
			log.log(Level.SEVERE, "Url is malformed", e);
		} catch (IOException e) {
			log.log(Level.SEVERE, "IOException", e);
		}
		return result;
	}

	public Vector<JSONObject> getEntriesByStudentAndCourse(String studentid, String courseid, String formatStr, int offset,
			int size) {
		Vector<JSONObject> results = new Vector<JSONObject>();
		if (offset < 0 || size < 1)
			return results;
		boolean export = false;
		if (formatStr != null && formatStr.equalsIgnoreCase("export")) {
			export = true;
		}
		try {
			DatastoreService datastore = DatastoreServiceFactory
					.getDatastoreService();
			Query query = null;
			query = new Query("Enrollment");
			if (studentid != null && !studentid.trim().equals("")) {
				query.addFilter("studentid", Query.FilterOperator.EQUAL, studentid);
			}
			if (courseid != null && !courseid.trim().equals("")) {
				query.addFilter("studentid", Query.FilterOperator.EQUAL, courseid);
			}
			List<Entity> result = datastore.prepare(query).asList(
					FetchOptions.Builder.withLimit(size).offset(offset));
			for (Entity entity : result) {
				JSONObject ent = new JSONObject();
				Map<String, Object> props = entity.getProperties();
				if (export) {
					ent.put("key", KeyFactory.keyToString(entity.getKey()));
					ent.put("entitytype", "student");
				}
				for (String key : props.keySet()) {
					Object object = props.get(key);
					String value = "";
					if (object != null)
						value = object.toString();
					if (object instanceof Text)
						value = ((Text) object).getValue();
					if (object instanceof Date)
						value = DateManager.fmt1.print(new DateTime(
								(Date) object));
					ent.put(key, value);
					if (export)
						ent.put(key + "_type", object.getClass()
								.getCanonicalName());
				}
				results.add(ent);
			}
		} catch (JSONException e) {
			log.log(Level.SEVERE, "JSONException", e);
		}
		return results;
	}
	
	public String JSONToString(Vector<JSONObject> objects){
		JSONObject json = new JSONObject();
		JSONArray results = new JSONArray(objects);
		try {
			json.put("total_results", results.length());
			json.put("results", results);
		} catch (JSONException e) {
			log.log(Level.SEVERE, "JSONException", e);
		}
		return json.toString();
	}

	public JSONObject getEntityById(String key){
		String[] splits = key.split("\\(|\\)");
		int id = Integer.parseInt(splits[1]);
		return getEntityById(splits[0], id);
	}
	
	public JSONObject getEntityById(String kind, int id){
		DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
		Key k = KeyFactory.createKey(kind, id);
		Entity ent;
		JSONObject result = new JSONObject();
		try {
			ent = datastore.get(k);
			Map<String, Object> props = new HashMap<String, Object>();
			props.putAll(ent.getProperties());
			props.put("id", ent.getKey());
			for (String key : props.keySet()) {
				Object object = props.get(key);
				String value = "";
				if (object != null)
					value = object.toString();
				if (object instanceof Text)
					value = ((Text) object).getValue();
				if (object instanceof Date)
					value = DateManager.fmt1.print(new DateTime(
							(Date) object));
				result.put(key, value);
			}
		} catch (EntityNotFoundException e1) {} 
		  catch (JSONException e) {}
		return result;
	}
	
	public Entity getSingleEntity(String key){
		String[] splits = key.split("\\(|\\)");
		int id = Integer.parseInt(splits[1]);
		String kind = splits[0];
		DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
		Key k = KeyFactory.createKey(kind, id);
		try {
			return datastore.get(k);
		} catch (EntityNotFoundException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	
	public String getEntityStringById(String kind, int id){
		return JSONToString(new Vector<JSONObject>(Arrays.asList(getEntityById(kind, id))));
	}

	public String JSONToString(JSONObject entityById) {
		// TODO Auto-generated method stub
		return null;
	}
}
